/*  WiFi softAP Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/sockets.h"
#include "cJSON.h"

#include "pan_main.h"
/* The examples use WiFi configuration that you can set via project configuration menu.

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/

static const char *TAG = "wifi softAP";

extern char SOFT_AP_SSID[20];
extern APP_INFO app_info;

void wifi_init_softap(void)
{
    esp_netif_create_default_wifi_ap();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    wifi_config_t wifi_config = {
        .ap = {
            .ssid_len = 0,
            .channel = 1, //??????
            .max_connection = 1,
            .authmode = WIFI_AUTH_OPEN},
    };
    memcpy(wifi_config.ap.ssid, SOFT_AP_SSID, sizeof(SOFT_AP_SSID));

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "SoftAP set finish:%s \n", SOFT_AP_SSID);
}

void create_ap_server(void *para)
{
    char addr_str[128];
    /*****************************create socket********************************/
    int listen_sock;
    int addr_family = AF_INET;
    int ip_protocol = IPPROTO_IP;
    while (1)
    {
        listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
        if (listen_sock < 0)
        {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            vTaskDelay(pdMS_TO_TICKS(200));
        }
        else
        {
            ESP_LOGI(TAG, "Socket created");
            break;
        }
    }
    /*****************************bound socket********************************/
    int err = 0;
    struct sockaddr_in dest_addr;
    dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(5050);
    inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
    while (1)
    {
        err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
        if (err != 0)
        {
            ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
            vTaskDelay(pdMS_TO_TICKS(200));
        }
        else
        {
            ESP_LOGI(TAG, "Socket bound, port %d", 5050);
            break;
        }
    }
    char pdata[256];
    char check = 0x00; //收到SERIAL_NUMBER 0x01;PASSWORD 0x02;SSID 0x04;SERVER_IP 0x08;SERVER_PORT 0x10
    char sdata[64];
    while (1)
    {
        /*****************************listen socket********************************/
        err = listen(listen_sock, 1);
        if (err != 0)
        {
            ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket listening");
        /*****************************accept socket********************************/
        struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
        uint addr_len = sizeof(source_addr);
        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (sock < 0)
        {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket accepted");
        while (1)
        {
            int len = recv(sock, pdata, sizeof(pdata) - 1, 0);
            /*****************************Error occurred during receiving********************************/
            if (len < 0)
            {
                ESP_LOGE(TAG, "recv failed: errno %d", errno);
                break;
            }
            /*****************************Connection closed********************************/
            else if (len == 0)
            {
                ESP_LOGI(TAG, "Connection closed");
                break;
            }
            /*****************************Data received********************************/
            else
            {
                // Get the sender's ip address as string
                if (source_addr.sin6_family == PF_INET)
                {
                    inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
                }
                else if (source_addr.sin6_family == PF_INET6)
                {
                    inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
                }
                cJSON *pJsonRoot = cJSON_Parse(pdata);
                if (!pJsonRoot)
                {
                    printf("Error before: [%s]\n", cJSON_GetErrorPtr());
                }
                else
                {
                    char *s = cJSON_Print(pJsonRoot);
                    ESP_LOGI(TAG, "pJsonRoot: %s\r\n", s);
                    cJSON_free((void *)s);
                    //解析SERIAL_NUMBER字段
                    cJSON *pSerialNumber = cJSON_GetObjectItem(pJsonRoot, "SERIAL_NUMBER");
                    if (pSerialNumber)
                    {
                        if (cJSON_IsString(pSerialNumber) && (12 == strlen(pSerialNumber->valuestring)))
                        {
                            memcpy(app_info.SerialNumber, pSerialNumber->valuestring, strlen(pSerialNumber->valuestring));
                            ESP_LOGI(TAG, "SerialNumber:%s \n", app_info.SerialNumber);
                            check = 0x01;
                        }
                        else
                        {
                            ESP_LOGI(TAG, "SerialNumber:format error");
                        }
                    }
                    //解析password字段
                    cJSON *pPassWord = cJSON_GetObjectItem(pJsonRoot, "PASSWORD");
                    if (pPassWord)
                    {
                        if (cJSON_IsString(pPassWord))
                            memcpy(app_info.password, pPassWord->valuestring, strlen(pPassWord->valuestring));
                        ESP_LOGI(TAG, "password:%s \n", app_info.password);
                        check |= 0x02;
                    }
                    //解析ssid字段
                    cJSON *pSSID = cJSON_GetObjectItem(pJsonRoot, "SSID");
                    if (pSSID)
                    {
                        if (cJSON_IsString(pSSID))
                            memcpy(app_info.ssid, pSSID->valuestring, strlen(pSSID->valuestring));
                        ESP_LOGI(TAG, "ssid:%s \n", app_info.ssid);
                        check |= 0x04;
                    }
                    //解析serverip字段
                    cJSON *pServerIP = cJSON_GetObjectItem(pJsonRoot, "SERVER_IP");
                    if (pServerIP)
                    {
                        if (cJSON_IsString(pServerIP))
                            memcpy(app_info.server_ip, pServerIP->valuestring, strlen(pServerIP->valuestring));
                        ESP_LOGI(TAG, "server_ip:%s \n", app_info.server_ip);
                        check |= 0x08;
                    }
                    //解析port字段
                    cJSON *pPort = cJSON_GetObjectItem(pJsonRoot, "SERVER_PORT");
                    if (pPort)
                    {
                        if (cJSON_IsString(pPort))
                            app_info.port = atoi(pPort->valuestring);
                        ESP_LOGI(TAG, "port:%d \n", app_info.port);
                        check |= 0x10;
                    }
                    cJSON_Delete(pJsonRoot); //释放内存
                    if ((0x01 == check) || (0x1E == check))
                    {
                        if (0x1E == check)
                            sendData("\xA5\x5A\x02\x00\x0C\x02", 6); //配网成功
                        else if (0x01 == check)
                            sendData("\xA5\x5A\x02\x00\x0C\x03", 6); //配序列号成功
                        //保存配网信息
                        ESP_ERROR_CHECK(user_nvs_save_blob(NVS_APP_INFO, &app_info, sizeof(app_info)));
                        //发送deviceid给设备
                        // sprintf(sdata, "{\"MAC\":\"%s\"}\r\n", macaddress);
                        // ESP_LOGI(TCP_TAG, "repeat to app:%s \n", sdata);
                        sprintf(sdata, "{\"ACK\":\"OK\"}");
                        ESP_LOGI(TAG, "repeat to app:%s \n", sdata);
                        send(sock, sdata, 12, 0);
                        vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s发一次
                        shutdown(sock, 0);
                        close(sock);
                        esp_restart(); //重启连接网络
                    }
                    else
                    {
                        check = 0;
                        ESP_LOGI(TAG, "check error");
                    }
                }
            }
        }
        shutdown(sock, 0);
        close(sock);
    }
}
